package org.dreamwork.tools.network.bridge.client.fx.js;

import com.google.gson.Gson;
import javafx.application.Platform;
import javafx.concurrent.Worker;
import javafx.scene.web.WebEngine;
import netscape.javascript.JSObject;
import org.dreamwork.tools.network.bridge.client.fx.DialogHelp;
import org.dreamwork.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

public final class JSBridge {
    private final Logger logger = LoggerFactory.getLogger (JSBridge.class);
    private WebEngine engine;
    private IJavaScriptListener listener;
    private IJavaScriptActionExecutor executor;
    private JSObject win;

    public JSBridge (WebEngine engine, String url, IJavaScriptListener listener) {
        this.engine = engine;
        this.listener = listener;
        engine.setJavaScriptEnabled (true);
/*
        URL resource = getClass ().getClassLoader ().getResource ("web-ui/css/metro/app.css");
        if (resource != null) {
            engine.setUserStyleSheetLocation (resource.toExternalForm ());
        }
*/
        createBridge ();
        engine.load (url);
    }

    public void setJavaScriptActionExecutor (IJavaScriptActionExecutor executor) {
        this.executor = executor;
    }

    @SuppressWarnings ("unused")
    public Object require (String action, Object params) {
        if (logger.isTraceEnabled ()) {
            logger.trace ("action = {}, paras = {}[{}]", action, params, (params != null ? params.getClass ().getName () : "<null>"));
        }
        Map<String, Object> map = new HashMap<> ();
        if (executor != null) {
            try {
                Object o = executor.perform (action, params);
                map.put ("code", 0);
                if (o != null) {
                    map.put ("data", o);
                }
            } catch (Exception ex) {
                logger.warn (ex.getMessage (), ex);
                map.put ("code", -1);
                map.put ("data", ex.getMessage ());
            }
        }

        return new Gson ().toJson (map);
    }

    public void log (String message) {
        if (logger.isTraceEnabled ()) {
            logger.trace (message);
        }
    }

    public void error (String message) {
        logger.error (message);
    }

    private void createBridge () {
        engine.setOnAlert (h->{
            String message = h.getData ();
            if (message != null) {
                try {
                    DialogHelp.info (message);
                } catch (Exception ex) {
                    logger.info (message);

                    logger.warn (ex.getMessage (), ex);
                }
            }
        });
        engine.setOnError (e->{
            String message = e.getMessage ();
            logger.error (message);
            Throwable ex = e.getException ();
            if (ex != null) {
                try {
                    DialogHelp.error (message, ex);
                } catch (Exception err) {
                    logger.error (message);
                    logger.warn (err.getMessage (), err);
                }
                logger.error (ex.getMessage (), ex);
            }
        });
        engine.getLoadWorker().stateProperty().addListener ((o, v, n) -> {
            if (Worker.State.SUCCEEDED == n) {
                win = (JSObject) engine.executeScript ("window");
                win.setMember ("jfx", this);
                win.setMember ("LOCALE", Locale.getDefault ().toString ());
                if (logger.isTraceEnabled ()) {
                    logger.trace ("java bridge connected");
                }
                win.eval ("bridge.init_page ()");

                if (listener != null) {
                    listener.onLoad (win);
                }
            }
        });
    }

    public void firePageLoaded (String data) {
        if (Platform.isFxApplicationThread ()) {
            eval ("bridge.fire_page_load ('" + data + "')");
        } else {
            Platform.runLater (()->eval ("bridge.fire_page_load ('" + data + "')"));
        }
    }

    public boolean confirmPageClose (String data) {
        String statement = "bridge.confirm_close (";
        if (!StringUtil.isEmpty (data)) {
            statement += "'" + data + "'";
        }
        statement += ")";
        Object o = eval (statement);

        if (o instanceof Boolean) {
            return (Boolean) o;
        }

        return true;
    }

    public Object call (String function, Object... arguments) {
        if (win == null) {
            throw new IllegalStateException ("web page is not loaded");
        }
        Object o = win.call (function, arguments);
        if (logger.isTraceEnabled ()) {
            logger.trace ("{} returns: {}", function, o);
        }
        return o;
    }

    public Object eval (String statement) {
        if (win == null) {
            throw new IllegalStateException ("web page is not loaded.");
        }

        return win.eval (statement);
    }
}